#include "relativo.h"

t_descriptorRel* archivosAbiertos[R_MAX_OPEN_FILES];

int R_CREATE (const char* nombre_fisico, int tam_registro, int max_reg)
{
	int fd,j;
	char *aux;
  char estadoCelda;

  /* Creo el nuevo archivo */
	fd = open(nombre_fisico, O_RDWR | O_CREAT | O_EXCL, 0666);

	if (fd<0) {
    if (errno == EEXIST)
      return RES_EXISTE;
    else
		  return RES_ERROR;
	}

  /* Guardo la cabecera del archivo */
  if (write(fd,(void *)tam_registro,sizeof(int))!=sizeof(int)) {
      close(fd);
      return RES_ERROR;
    }
  if (write(fd,(void *)max_reg,sizeof(int))!=sizeof(int)) {
      close(fd);
      return RES_ERROR;
    }
    
  /* Preparo las celdas */
	aux = (char*)malloc(tam_registro);
	estadoCelda = R_LIBRE;
	for(j=0; j<max_reg; j++) {
    /* Guardo el dato administrativo */
		if (write(fd, (char*)&estadoCelda, sizeof(char)) != sizeof(char)) {
			free(aux);
      close(fd);
			return RES_ERROR;
		}

    /* Guardo la seccion de datos*/
		if (write(fd, aux, tam_registro) != tam_registro) {
			free(aux);
      close(fd);
			return RES_ERROR;
		}
	}

	free(aux);
  close(fd);

  return RES_OK;
}

int R_OPEN (const char* nombre_fisico, int modo)
{
	int fd,i,openmodo;
	t_descriptorRel* nuevo_arch;
	int long_reg, max_reg;

  /* Busco un file descriptor libre */
	i=0;
	while ((i<R_MAX_OPEN_FILES)&&(archivosAbiertos[i]!=NULL)) {
 		i++;
	}
 	if (i>=R_MAX_OPEN_FILES) {
		return RES_NULL;
	}

  /* Convierto el modo de apertura al interno */
  switch (modo) {
    case READ:
      openmodo = O_RDONLY;
      break;
    case WRITE:
      openmodo = O_WRONLY;
      break;
    case READ_WRITE:
      openmodo = O_RDWR;
      break;
    default:
      return RES_NULL;
    }
    
	fd = open(nombre_fisico,openmodo);
	if (fd<0) {
		return RES_NULL;
	}

  /* Leo la cabecera */ 
	if (read(fd,(void *)long_reg,sizeof(int))!=sizeof(int)) {
    close(fd);
		return RES_NULL;
  }

	if (read(fd,(void *)max_reg,sizeof(int))!=sizeof(int)) {
    close(fd);
		return RES_NULL;
	}

  /* Guardo la entrada en el vector de archivos relativos abiertos */
	nuevo_arch = (t_descriptorRel *)malloc(sizeof(t_descriptorRel));
	nuevo_arch->fd=fd;
	nuevo_arch->long_reg=long_reg;
	nuevo_arch->max_reg=max_reg;
	archivosAbiertos[i]=nuevo_arch;
	return i;
}

int R_CLOSE (int handler)
{
	int ok;
  /* Cierro el archivo */
	ok = close(archivosAbiertos[handler]->fd);
	if (ok<0) {
 		return RES_ERROR;
	}

  /* Devuelvo la entrada en el vector de archivos abiertos */
	free(archivosAbiertos[handler]);
	archivosAbiertos[handler]=NULL;

	return RES_OK;
}

int R_READ(int handler, int nrec, void* reg)
{
	int pos, res, long_celda;
	t_descriptorRel *arch;
	char estadoCelda;

	arch = archivosAbiertos[handler];
	if (nrec >= arch->max_reg)	{
		return RES_NO_EXISTE;
	}

  /* El longitud de la celda es la del registro mas el dato administrativo */
	long_celda = sizeof(char) + arch->long_reg;
  /* Calculo de la posicion (teniendo en cuenta datos administrativos)*/
	pos = (long_celda*nrec) + 2*sizeof(int);
	res = lseek(arch->fd, pos, SEEK_SET);
	if (res<0) {
		return RES_ERROR;
	}

  /* Una vez posicionado leo el registro */
	res = read(arch->fd, (char *) &estadoCelda, sizeof(char));
	if (res == sizeof(char) && estadoCelda == R_OCUPADO) {
		res = read(arch->fd, reg, arch->long_reg);
		if (res == arch->long_reg) {
			return RES_OK;
		}
	}	

	return RES_ERROR;
}

int R_READNEXT (int handler, void* reg)
{
	int res1, res2, long_celda;
	t_descriptorRel *arch;
	char estadoCelda;

	arch = archivosAbiertos[handler];

	long_celda = sizeof(char) + arch->long_reg;
  /* Leo desde donde estoy hasta encontrar una celda ocupada */
	do {
		res1 = read(arch->fd, (char *)&estadoCelda, sizeof(char));
		res2 = read(arch->fd, reg, arch->long_reg);
	} while ((res1==sizeof(char)) && (estadoCelda!=R_OCUPADO));

	if (res2 == arch->long_reg) {
		return RES_OK;
	}

	return RES_ERROR;
}

int R_WRITE (int handler, int nrec, const void* reg)
{
	int pos, res, long_celda;
	t_descriptorRel *arch;
	char estadoCelda;

	arch = archivosAbiertos[handler];
	if (nrec >= arch->max_reg)	{
		return RES_NO_EXISTE;
	}

  /* Me posiciono en la celda */
	long_celda = sizeof(char) + arch->long_reg;
	pos = (long_celda*nrec + 2*sizeof(int));
	res = lseek(arch->fd, pos, SEEK_SET);
	if (res<0) {
		return RES_ERROR;
	}

  /* Me fijo que no este ocupada */
	res = read(arch->fd, (char *)&estadoCelda, sizeof(char));
	if ((res == sizeof(char))) {
    if (estadoCelda == R_OCUPADO)
      return RES_EXISTE;

		res = lseek(arch->fd, pos, 0);
		if (res<0) {
			return RES_ERROR;
		}

    /* Guardo el registro en la celda */
		estadoCelda=R_OCUPADO;
		res = write(arch->fd, (char *)&estadoCelda, sizeof(char));

		if (res == sizeof(char)) {
			res = write(arch->fd, reg, arch->long_reg);
			
			if (res==arch->long_reg) {
				return RES_OK;
			}
		}
    }	

	return RES_ERROR;
}
		   
int R_SEEK (int handler, int nrec)
{
	int pos, res, long_celda;
	t_descriptorRel *arch;

	arch = archivosAbiertos[handler];

	if (nrec >= arch->max_reg) {
		return RES_NO_EXISTE;
	}

  /* Me posiciono en la celda */
  long_celda = sizeof(char) + arch->long_reg;
	pos = (long_celda*nrec) + 2*sizeof(int);
	res = lseek(arch->fd ,pos, 0);
	if (res<0) {
		return RES_ERROR;
	}

	return RES_OK;
}

int R_UPDATE (int handler, int nrec, const void* reg)
{
	int pos, res, long_celda;
	t_descriptorRel *arch;
	char estadoCelda;

	arch = archivosAbiertos[handler];
	if (nrec >= arch->max_reg)	{
		return RES_NO_EXISTE;
	}

  /* Me posiciono en la celda */
	long_celda = sizeof(char) + arch->long_reg;
	pos = (long_celda*nrec + 2*sizeof(int));
	res = lseek(arch->fd, pos, 0);
	if (res<0) {
		return RES_ERROR;
	}

  /* Verifico que tenga algun registro cargado */
	res = read(arch->fd, (char *)&estadoCelda, sizeof(char));
	if ((res == sizeof(char))) {

    if  (estadoCelda != R_OCUPADO)
      return RES_NO_EXISTE;

    /* Grabo el registro */  
	  res = write(arch->fd, reg, arch->long_reg);
    	
			if (res==arch->long_reg)
				return RES_OK;
	  }

	return RES_ERROR;
}

int R_DELETE (int handler, int nrec)
{
	int pos, res, long_celda;
	t_descriptorRel *arch;
	char estadoCelda;

	arch = archivosAbiertos[handler];
	if (nrec >= arch->max_reg)	{
		return RES_NO_EXISTE;
	}

  /* Me posiciono en la celda */
	long_celda = sizeof(char) + arch->long_reg;
	pos = (long_celda*nrec + 2*sizeof(int));
	res = lseek(arch->fd, pos, 0);
	if (res<0) {
		return RES_ERROR;
	}

  /* Verifico que tenga algun valor */
	res = read(arch->fd, (char *)&estadoCelda, sizeof(char));
	if (res == sizeof(estadoCelda)) {

    if (estadoCelda == R_OCUPADO)
      return RES_NO_EXISTE;
      
		res = lseek(arch->fd, pos, 0);
		if (res<0) {
			return RES_ERROR;
		}

    /* Marco la celda como libre */
    estadoCelda=R_LIBRE;
		res = write(arch->fd, (char *)&estadoCelda, sizeof(char));

		if (res == sizeof(char))
			return RES_OK;
		
  }	

	return RES_ERROR;
}

int R_GETMAXREGS( int handler ) {
    if (archivosAbiertos[handler]==NULL)
      return RES_ERROR;

    /* Retorno la cantidad total de registros */      
    return archivosAbiertos[handler]->max_reg;
}

int R_DESTROY(const char* nombre_fisico) {
  /* Elimino el archivo */
  if (remove(nombre_fisico)==0)
    return RES_OK;
  else
    return RES_ERROR;
}

